﻿using Batman.Authentication;
using Batman.Authentication.Dtos;
using Batman.Authentication.Extension;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class AuthenticationServiceCollectionExtensions
    {
        public static void AddBatmanAuthentication(this IServiceCollection services, string rmsUrl, string appCode, Type startupType, string secret = null, string issuer = null, string audience = null)
        {
            if (secret != null) JwtBearerSetting.secret = secret;
            if (issuer != null) JwtBearerSetting.valid_issuer = issuer;
            if (audience != null) JwtBearerSetting.valid_audience = audience;

            ApplicationOptions.AppCode = appCode;
            ApplicationOptions.RmsUrl = rmsUrl;

            #region 配置jwtbearer

            Action<JwtBearerOptions> configureOptions = options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidAudience = JwtBearerSetting.valid_audience,
                    ValidIssuer = JwtBearerSetting.valid_issuer,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtBearerSetting.secret))
                };
            };

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                    .AddJwtBearer(configureOptions)
            // Microsoft.AspNetCore.Identity SignInManager需要
                    .AddCookie("Identity.Application");

            #endregion

            services.AddMvc(options =>
            {
                // 权限控制
                options.Filters.Add<Batman.Authentication.AuthorizationFilter>();

            }).SetCompatibilityVersion(AspNetCore.Mvc.CompatibilityVersion.Version_2_2);


            FishingResources(startupType);  // 从项目中把资源捞出来
            Submit();            // 提交权限
        }


        /// <summary>
        /// 提交资源
        /// </summary>
        static void Submit()
        {

        }

        /// <summary>
        /// 从项目中捞资源
        /// </summary>
        /// <returns></returns>
        static IEnumerable<ResourceAttribute> FishingResources(Type startupType)
        {
            var exportTypes = startupType.Assembly.ExportedTypes;

            foreach (var exportType in exportTypes)
            {
                if (!exportType.IsSubclassOf(typeof(ControllerBase))) continue;

                var methodInfos = exportType.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);

                foreach (var methodInfo in methodInfos)
                {
                    var resource = methodInfo.GetCustomAttributes(typeof(ResourceAttribute), false).FirstOrDefault() as ResourceAttribute;
                    if (resource == null) continue;

                    if (string.IsNullOrEmpty(resource.Url))
                        resource.Url = ParseUrl(exportType, methodInfo, resource);

                    yield return resource;
                }
            }
        }

        static string[] ResfulMethod = new string[] { "get", "post", "put", "delete" };

        /// <summary>
        /// 根据mvc路由规则，获取资源路径
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        static string ParseUrl(Type controller, MemberInfo action, ResourceAttribute resource)
        {
            var controllerName = controller.Name.Replace("Controller", string.Empty);

            var routeAttr = controller.GetCustomAttribute<RouteAttribute>();
            if (routeAttr != null)
                controllerName = Regex.Replace(routeAttr.Template, @"\[controller\]", controllerName, RegexOptions.IgnoreCase);


            var actionName = action.Name;
            if (resource.IsResful)
            {
                if (ResfulMethod.Contains(actionName.ToLower()))
                {
                    switch (actionName.ToLower())
                    {
                        case "get":
                            resource.Method = ResourceMethod.get;
                            break;
                        case "post":
                            resource.Method = ResourceMethod.post;
                            break;
                        case "put":
                            resource.Method = ResourceMethod.put;
                            break;
                        case "delete":
                            resource.Method = ResourceMethod.delete;
                            break;
                    }

                    actionName = string.Empty;
                }
            }

            return $"/{controllerName}/{actionName}".Replace("//", "/");
        }
    }
}
